classdef MultiLabelFilter ...
        < Filter ...
%         & Algorithm0able
    %MULTILABELFILTER Summary of this class goes here
    %   Detailed explanation goes here
    
    properties ( SetAccess = protected )
        strategy = 'max' % 'min' 'max' 'mean'|'avg'
    end
    
    methods
        function [  ] = setStrategy( this, strategy )
            if nargin == 1
                this.strategy = 'max';
            end
            if nargin == 2
                this.strategy = strategy;
            end
        end
    end
    
    methods
        function [ this ] = MultiLabelFilter( name )
            if nargin == 0
                this.setName('mlabel_filter');
            end
            if nargin >= 1
                this.setName(name);
            end
            %TODO
        end
    end
    
    methods
        function [  ] = build( this, X1, Y1 )
            nFeature = size(X1, 2);
            zFeatureScore = zeros(1, nFeature);
            nLabel = size(Y1, 2);
            
            % Calculate the converted score of each feature to the whole
            % label set
            for iFeature = 1:nFeature
                zScore_i = zeros(1, nLabel);
                
                % i-th feature to each label
                for jLabel = 1:nLabel
                    zScore_i(jLabel) = this.algorithm0.apply(X1(:, iFeature), Y1(:, jLabel));
                end
                
                switch this.strategy
                    case {'min', 'minimum'}
                        zFeatureScore(iFeature) = min(zScore_i);
                    case {'max', 'maximum'}
                        zFeatureScore(iFeature) = max(zScore_i);
                    case {'avg', 'average', 'mean'}
                        zFeatureScore(iFeature) = mean(zScore_i);
                    otherwise
                        error('BatErr:!!!');
                end
            end
            
            this.model.zScore = zFeatureScore;%%%TODO: Need sorting?
            [~, this.model.zRank] = sort(zFeatureScore, 'descend');
        end
    end
    
    methods
        function [ zRank, zScore ] = searchBackward( this, X, Y, strategy )
            nFeature = size(X, 2);
            % nLabel = size(Y, 2);
            zRank = zeros(1, nFeature);
            zScore = zeros(1, nFeature);
            
            zSelectedId = true(1, nFeature);
            nthFeature = nnz(zSelectedId);
            while nthFeature > 1
                fprintf(1, '%d, ', nthFeature);
                z_metric_i = -inf(1, nFeature);
                for iFeature = find(zSelectedId)
                    z_id_i = zSelectedId; z_id_i(iFeature) = false;
                    X_i = X(:, z_id_i);
                    temp_metric_i = this.algorithm0.calc(X_i, Y);
                    if isequal(strategy, 'max')
                        z_metric_i(iFeature) = max(temp_metric_i);
                    elseif isequal(strategy, 'avg')
                        z_metric_i(iFeature) = mean(temp_metric_i);
                    else
                        z_metric_i(iFeature) = feval(strategy, temp_metric_i);
                    end
                end
                [max_val_i, max_id_i] = max(z_metric_i); max_id_i = max_id_i(1);
                zRank(nthFeature) = max_id_i;
                zScore(nthFeature) = max_val_i;
                zSelectedId(max_id_i) = false;
                nthFeature = nthFeature - 1;
            end
            zRank(1) = find(zSelectedId);
            temp_metric_i = this.algorithm0.calc(X(:, zRank(1)), Y);
            if isequal(strategy, 'max')
                zScore(1) = max(temp_metric_i);
            elseif isequal(strategy, 'avg')
                zScore(1) = mean(temp_metric_i);
            else
                z_metric_i(iFeature) = feval(strategy, temp_metric_i);
            end
        end
        
        function [ zRank, zScore ] = searchForward( this, X, Y, strategy )
            nFeature = size(X, 2);
            zRank = zeros(1, nFeature);
            zScore = zeros(1, nFeature);
            
            zSelectedId = false(1, nFeature);
            nthFeature = 1;
            nLabel = size(Y, 2);
            last_temp_metric = zeros(1, nLabel);
            while nthFeature <= nFeature
                fprintf(1, '%d, ', nthFeature);
                z_metric_i = -inf(1, nFeature);
                for iFeature = find(~zSelectedId)
                    z_id_i = zSelectedId; z_id_i(iFeature) = true;
                    X_i = X(:, z_id_i);
                    temp_metric_i = this.algorithm0.calc(X_i, Y);
                    if isequal(strategy, 'max')
                        z_metric_i(iFeature) = max(temp_metric_i);
                    elseif isequal(strategy, 'avg')
                        z_metric_i(iFeature) = mean(temp_metric_i);
                    else
                        z_metric_i(iFeature) = feval(strategy, temp_metric_i, last_temp_metric);
                    end
                end
                [max_val_i, max_id_i] = max(z_metric_i); max_id_i = max_id_i(1);
                zRank(nthFeature) = max_id_i;
                zScore(nthFeature) = max_val_i;
                zSelectedId(max_id_i) = true;
                nthFeature = nthFeature + 1;
                
                X_i = X(:, zSelectedId);
                last_temp_metric = this.algorithm0.calc(X_i, Y);
            end
        end
    end
    
end

